#include <GameEngine/Core/Color/color.h>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

//=========================================================================================

//! Convert the HSV components of a color to the RGB space
/*!
 *  Please note that not all hue, saturation and value triplets necessarily have an equivalent RGB color.
 *
 @param h The hue component of the color
 @param s The saturation component of the color
 @param v The value component of the color
 @param red Receives the computed red component of the color, in range [0,1]
 @param green Receives the computed green component of the color, in range [0,1]
 @param blue Receives the computed blue component of the color, in range [0,1]
 @return True if RGB components of the color could be computed from this function, false
         otherwise. RGB components may not be computed if the HSV components do not have any equivalent
		 in the RGB space.
 */
bool Color::hsv_to_rgb(float h, float s, float v, float& red, float& green, float& blue) {
	// Set color as a level of gray
	if ( s <= 0.0f ) {
		red = v;
		green = v;
		blue = v;
		if ( v < 0.0f ) return false;
		if ( v > 1.0f ) return false;
		return true;
	}

	// Color is actually NOT a level of gray
	// Setup usefaul variables
	int i = (int)(6.0f*h);
	float var1 = v * ( 1.0f - s );
	float var2 = v * ( 1.0f - 6.0f*h*s + (float)i*s );
	float var3 = v * ( 1.0f + 6.0f*h*s - (1.0f+(float)i)*s );

	// Separate different cases
	if ( i == 0 ) {
		red = v;
		green = var3;
		blue = var1;
	}
	else if ( i == 1 ) {
		red = var2;
		green = v;
		blue = var1;
	}
	else if ( i == 2 ) {
		red = var1;
		green = v;
		blue = var3;
	}
	else if ( i == 3 ) {
		red = var1;
		green = var2;
		blue = v;
	}
	else if ( i == 4 ) {
		red = var3;
		green = var1;
		blue = v;
	}
	else {
		red = v;
		green = var1;
		blue = var2;
	}

	// Check color a-posteriori
	if ( red < 0.0f ) return false;
	if ( red > 1.0f ) return false;
	if ( green < 0.0f ) return false;
	if ( green > 1.0f ) return false;
	if ( blue < 0.0f ) return false;
	if ( blue > 1.0f ) return false;
	return true;
}

//! Convert the CMYK components of a color to the RGB space
/*!
 *  Please note that not all CMYK quadruplets necessarily have an equivalent RGB color.
 *
 @param c The cyan component of the color
 @param m The magenta component of the color
 @param y The yellow component of the color
 @param k The black component of the color
 @param red Receives the computed red component of the color, in range [0,1]
 @param green Receives the computed green component of the color, in range [0,1]
 @param blue Receives the computed blue component of the color, in range [0,1]
 @return True if RGB components of the color could be computed from this function, false
         otherwise.
 */
bool Color::cmyk_to_rgb(float c, float m, float y, float k, float& red, float& green, float& blue) {
	// Setup RGB components
	red   = 1.0f - ( k + c*(1.0f-k) );
	green = 1.0f - ( k + m*(1.0f-k) );
	blue  = 1.0f - ( k + y*(1.0f-k) );

	// Check color a-posteriori
	if ( red < 0.0f ) return false;
	if ( red > 1.0f ) return false;
	if ( green < 0.0f ) return false;
	if ( green > 1.0f ) return false;
	if ( blue < 0.0f ) return false;
	if ( blue > 1.0f ) return false;
	return true;
}

//! Convert the YUV components of a color to the RGB space
/*!
 *  Please note that not all YUV triplets necessarily have an equivalent RGB color.
 *
 @param y The Y component of the color
 @param u The U component of the color
 @param v The V component of the color
 @param red Receives the computed red component of the color, in range [0,1]
 @param green Receives the computed green component of the color, in range [0,1]
 @param blue Receives the computed blue component of the color, in range [0,1]
 @return True if RGB components of the color could be computed from this function, false
         otherwise.
 */
bool Color::yuv_to_rgb(float y, float u, float v, float& red, float& green, float& blue) {
	// Setup RGB components
	red   = 1.0f*y + 0.00000f*u + 1.13983f*v;
	green = 1.0f*y - 0.39465f*u - 0.58060f*v;
	blue  = 1.0f*y + 2.03211f*u + 0.00000f*v;

	// Check color a-posteriori
	if ( red < 0.0f ) return false;
	if ( red > 1.0f ) return false;
	if ( green < 0.0f ) return false;
	if ( green > 1.0f ) return false;
	if ( blue < 0.0f ) return false;
	if ( blue > 1.0f ) return false;
	return true;
}

//! Convert the CIE-XYZ components of a color to the RGB space
/*!
 *  Please note that not all CIE-XYZ triplets necessarily have an equivalent RGB color.
 *
 @param x The X component of the color
 @param y The Y component of the color
 @param z The Z component of the color
 @param red Receives the computed red component of the color, in range [0,1]
 @param green Receives the computed green component of the color, in range [0,1]
 @param blue Receives the computed blue component of the color, in range [0,1]
 @return True if RGB components of the color could be computed from this function, false
         otherwise.
 @note Thanks to Dr Hagit Hel-Or for providing color conversion functions on his webpage. These conversion
       functions can be accessed here:
	   http://cs.haifa.ac.il/hagit/courses/ist/Lectures/Demos/ColorApplet2/t_convert.html
 */
bool Color::ciexyz_to_rgb(float x, float y, float z, float& red, float& green, float& blue) {
	// Setup RGB components
	red   =  3.240479f*x - 1.537150f*y - 0.498535f*z;
	green = -0.969256f*x + 1.875992f*y + 0.041556f*z;
	blue  =  0.055648f*x - 0.204043f*y + 1.057311f*z;

	// Check color a-posteriori
	if ( red < 0.0f ) return false;
	if ( red > 1.0f ) return false;
	if ( green < 0.0f ) return false;
	if ( green > 1.0f ) return false;
	if ( blue < 0.0f ) return false;
	if ( blue > 1.0f ) return false;
	return true;
}

//! Convert the CIE-LAB components of a color to the RGB space
/*!
 *  Please note that not all CIE-LAB triplets necessarily have an equivalent RGB color.
 *
 @param l The L component of the color
 @param a The a component of the color
 @param b The b component of the color
 @param red Receives the computed red component of the color, in range [0,1]
 @param green Receives the computed green component of the color, in range [0,1]
 @param blue Receives the computed blue component of the color, in range [0,1]
 @return True if RGB components of the color could be computed from this function, false
         otherwise.
 @note Thanks to Dr Hagit Hel-Or for providing color conversion functions on his webpage. These conversion
       functions can be accessed here:
	   http://cs.haifa.ac.il/hagit/courses/ist/Lectures/Demos/ColorApplet2/t_convert.html
 */
bool Color::cielab_to_rgb(float l, float a, float b, float& red, float& green, float& blue) {
	// Get CIE-XYZ color
	float p = ( l + 16.0f ) / 116.0f;
	float x = 0.950456f * ( p + a / 500.0f ) * ( p + a / 500.0f ) * ( p + a / 500.0f );
	float y = p*p*p;
	float z = 1.08875f * ( p - b / 200.0f ) * ( p - b / 200.0f ) * ( p - b / 200.0f );

	// Setup RGB components
	return ciexyz_to_rgb(x,y,z,red,green,blue);
}

//=========================================================================================

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif
